home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume18 / elm2.2 / part16 < prev    next >
Encoding:
Internet Message Format  |  1989-04-12  |  49.4 KB

  1. Subject:  v18i095:  Elm mail system, release 2.2, Part16/24
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: dsinc!syd@uunet.UU.NET (Syd Weinstein)
  7. Posting-number: Volume 18, Issue 95
  8. Archive-name: elm2.2/part16
  9.  
  10. #!/bin/sh
  11. # this is part 16 of a multipart archive
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file src/leavembox.c continued
  14. #
  15. CurArch=16
  16. if test ! -r s2_seq_.tmp
  17. then echo "Please unpack part 1 first!"
  18.      exit 1; fi
  19. ( read Scheck
  20.   if test "$Scheck" != $CurArch
  21.   then echo "Please unpack part $Scheck next!"
  22.        exit 1;
  23.   else exit 0; fi
  24. ) < s2_seq_.tmp || exit 1
  25. echo "x - Continuing file src/leavembox.c"
  26. sed 's/^X//' << 'SHAR_EOF' >> src/leavembox.c
  27. X *******************************************************************************
  28. X * $Log:    leavembox.c,v $
  29. X * Revision 2.26  89/03/25  21:46:33  syd
  30. X * Initial 2.2 Release checkin
  31. X * 
  32. X *
  33. X ******************************************************************************/
  34. X
  35. X/** leave current folder, updating etc. as needed...
  36. X  
  37. X**/
  38. X
  39. X#include "headers.h"
  40. X#include <sys/types.h>
  41. X#include <sys/stat.h>
  42. X#ifdef LOCK_BY_FLOCK
  43. X#include <sys/file.h>
  44. X#endif
  45. X#include <errno.h>
  46. X
  47. X/**********
  48. X   Since a number of machines don't seem to bother to define the utimbuf
  49. X   structure for some *very* obscure reason.... 
  50. X
  51. X   Suprise, though, BSD has a different utime() entirely...*sigh*
  52. X**********/
  53. X
  54. X#ifndef BSD
  55. X# ifdef NOUTIMBUF
  56. X
  57. Xstruct utimbuf {
  58. X    time_t    actime;        /** access time       **/ 
  59. X    time_t    modtime;    /** modification time **/
  60. X       };
  61. X
  62. X
  63. X# endif /* NOUTIMBUF */
  64. X#endif /* BSD */
  65. X
  66. Xextern int errno;
  67. X
  68. Xchar *error_name(), *error_description(), *strcpy();
  69. Xunsigned short getegid();
  70. Xunsigned long sleep();
  71. X
  72. Xint
  73. Xleave_mbox(resyncing, quitting, prompt)
  74. Xint resyncing, quitting, prompt;
  75. X{
  76. X    /** Close folder, deleting some messages, storing others in mbox,
  77. X        and keeping others, as directed by user input and elmrc options.
  78. X
  79. X        Return    1    Folder altered
  80. X            0    Folder not altered
  81. X            -1    New mail arrived during the process and
  82. X                    closing was aborted.
  83. X        If "resyncing" we are just writing out folder to reopen it. We
  84. X        therefore only consider deletes and keeps, not stores to mbox.
  85. X        Also we don't remove NEW status so that it can be preserved
  86. X        across the resync.
  87. X
  88. X        If "quitting" prompting for user input is based on truth of
  89. X            "prompt". Otherwise prompting is dependent upon the variable
  90. X        question_me, as set by an elmrc option.
  91. X        This is because when called as part of quitting, the user
  92. X        selects run-time between the 'q' and 'Q' commands to tell
  93. X        elm whether s/he wants to be prompted or not. This would
  94. X        render use of the elmrc option moot (i.e. overridden).
  95. X        Prompted quit vs quick quit.
  96. X    **/
  97. X
  98. X    FILE *temp;
  99. X    char temp_keep_file[SLEN], buffer[SLEN];
  100. X    struct stat    buf;        /* stat command  */
  101. X#ifdef BSD
  102. X    time_t utime_buffer[2];        /* utime command */
  103. X#else
  104. X    struct utimbuf utime_buffer;    /* utime command */
  105. X#endif
  106. X    register int to_delete = 0, to_store = 0, to_keep = 0, i,
  107. X             marked_deleted, marked_read, marked_unread,
  108. X             last_sortby, ask_questions,  asked_storage_q,
  109. X             num_chgd_status, need_to_copy;
  110. X    char answer;
  111. X    long bytes();
  112. X
  113. X    dprint(1, (debugfile, "\n\n-- leaving folder --\n\n"));
  114. X
  115. X    if (message_count == 0)
  116. X      return(0);    /* nothing changed */
  117. X
  118. X    ask_questions = (quitting ? prompt : question_me);
  119. X
  120. X    /* YES or NO on softkeys */
  121. X    if (hp_softkeys && ask_questions) {
  122. X      define_softkeys(YESNO);
  123. X      softkeys_on();
  124. X    }
  125. X
  126. X    /* Clear the exit dispositions of all messages, just in case
  127. X     * they were left set by a previous call to this function
  128. X     * that was interrupted by the receipt of new mail.
  129. X     */
  130. X    for(i = 0; i < message_count; i++)
  131. X      headers[i]->exit_disposition = UNSET;
  132. X      
  133. X    /* Determine if deleted messages are really to be deleted */
  134. X
  135. X    /* we need to know if there are none, or one, or more to delete */
  136. X    for (marked_deleted=0, i=0; i<message_count && marked_deleted<2; i++)
  137. X      if (ison(headers[i]->status, DELETED))
  138. X        marked_deleted++;
  139. X
  140. X        if(marked_deleted) {
  141. X      answer = (always_del ? 'y' : 'n');    /* default answer */
  142. X      if(ask_questions) {
  143. X        sprintf(buffer, "Delete message%s? (y/n) ", plural(marked_deleted));
  144. X        answer = want_to(buffer, answer);
  145. X      }
  146. X
  147. X      if(answer == 'y') {
  148. X        for (i = 0; i < message_count; i++) {
  149. X          if (ison(headers[i]->status, DELETED)) {
  150. X        headers[i]->exit_disposition = DELETE;
  151. X        to_delete++;
  152. X          }
  153. X        }
  154. X      }
  155. X    }
  156. X    dprint(3, (debugfile, "Messages to delete: %d\n", to_delete));
  157. X
  158. X    /* If this is a non spool file, or if we are merely resyncing,
  159. X     * all messages with an unset disposition (i.e. not slated for
  160. X     * deletion) are to be kept.
  161. X     * Otherwise, we need to determine if read and unread messages
  162. X     * are to be stored or kept.
  163. X     */
  164. X    if(folder_type == NON_SPOOL || resyncing) {
  165. X      to_store = 0;
  166. X      for (i = 0; i < message_count; i++) {
  167. X        if(headers[i]->exit_disposition == UNSET) {
  168. X          headers[i]->exit_disposition = KEEP;
  169. X          to_keep++;
  170. X        }
  171. X      }
  172. X    } else {
  173. X
  174. X      /* Let's first see if user wants to store read messages 
  175. X       * that aren't slated for deletion */
  176. X
  177. X      asked_storage_q = FALSE;
  178. X
  179. X      /* we need to know if there are none, or one, or more marked read */
  180. X      for (marked_read=0, i=0; i < message_count && marked_read < 2; i++) {
  181. X        if((isoff(headers[i]->status, UNREAD))
  182. X          && (headers[i]->exit_disposition == UNSET))
  183. X        marked_read++;
  184. X      }
  185. X      if(marked_read) {
  186. X        answer = (always_store ? 'y' : 'n');    /* default answer */
  187. X        if(ask_questions) {
  188. X          sprintf(buffer, "Store read message%s in \"received\" folder? (y/n) ",
  189. X            plural(marked_read));
  190. X          answer = want_to(buffer, answer);
  191. X          asked_storage_q = TRUE;
  192. X        }
  193. X
  194. X        for (i = 0; i < message_count; i++) {
  195. X          if((isoff(headers[i]->status, UNREAD)) 
  196. X        && (headers[i]->exit_disposition == UNSET)) {
  197. X
  198. X          if(answer == 'y') {
  199. X            headers[i]->exit_disposition = STORE;
  200. X            to_store++;
  201. X          } else {
  202. X            headers[i]->exit_disposition = KEEP;
  203. X            to_keep++;
  204. X          }
  205. X          }
  206. X        } 
  207. X      }
  208. X
  209. X      /* If we asked the user if read messages should be stored,
  210. X       * and if the user wanted them kept instead, then certainly the
  211. X       * user would want the unread messages kept as well.
  212. X       */
  213. X      if(asked_storage_q && answer == 'n') {
  214. X
  215. X        for (i = 0; i < message_count; i++) {
  216. X          if((ison(headers[i]->status, UNREAD))
  217. X        && (headers[i]->exit_disposition == UNSET)) {
  218. X          headers[i]->exit_disposition = KEEP;
  219. X          to_keep++;
  220. X          }
  221. X        }
  222. X
  223. X      } else {
  224. X
  225. X        /* Determine if unread messages are to be kept */
  226. X
  227. X        /* we need to know if there are none, or one, or more unread */
  228. X        for (marked_unread=0, i=0; i<message_count && marked_unread<2; i++)
  229. X          if((ison(headers[i]->status, UNREAD))
  230. X        && (headers[i]->exit_disposition == UNSET))
  231. X          marked_unread++;
  232. X
  233. X        if(marked_unread) {
  234. X          answer = (always_keep ? 'y' : 'n');    /* default answer */
  235. X          if(ask_questions) {
  236. X        sprintf(buffer,
  237. X          "Keep unread message%s in incoming mailbox? (y/n) ",
  238. X          plural(marked_unread));
  239. X        answer = want_to(buffer, answer);
  240. X          }
  241. X
  242. X          for (i = 0; i < message_count; i++) {
  243. X        if((ison(headers[i]->status, UNREAD))
  244. X          && (headers[i]->exit_disposition == UNSET)) {
  245. X
  246. X            if(answer == 'n') {
  247. X              headers[i]->exit_disposition = STORE;
  248. X              to_store++;
  249. X            } else {
  250. X              headers[i]->exit_disposition = KEEP;
  251. X              to_keep++;
  252. X            }
  253. X          
  254. X        }
  255. X          }
  256. X        }
  257. X      }
  258. X    }
  259. X
  260. X    dprint(3, (debugfile, "Messages to store: %d\n", to_store));
  261. X    dprint(3, (debugfile, "Messages to keep: %d\n", to_keep));
  262. X
  263. X    if(to_delete + to_store + to_keep != message_count) {
  264. X      dprint(1, (debugfile,
  265. X      "Error: %d to delete + %d to store + %d to keep != %d message cnt\n",
  266. X        to_delete, to_store, to_keep, message_count));
  267. X      error("Something wrong in message counts! Folder unchanged.");
  268. X      emergency_exit();
  269. X    }
  270. X      
  271. X
  272. X    /* If we are not resyncing, we are leaving the mailfile and
  273. X     * the new messages are new no longer. Note that this changes
  274. X     * their status.
  275. X     */
  276. X    if(!resyncing) {
  277. X      for (i = 0; i < message_count; i++) {
  278. X        if (ison(headers[i]->status, NEW)) {
  279. X          clearit(headers[i]->status, NEW);
  280. X          headers[i]->status_chgd = TRUE;
  281. X        }
  282. X      }
  283. X    }
  284. X
  285. X    /* If all messages are to be kept and none have changed status
  286. X     * we don't need to do anything because the current folder won't
  287. X     * be changed by our writing it out - unless we are resyncing, in
  288. X     * which case we force the writing out of the mailfile.
  289. X     */
  290. X
  291. X    for (num_chgd_status = 0, i = 0; i < message_count; i++)
  292. X      if(headers[i]->status_chgd == TRUE)
  293. X        num_chgd_status++;
  294. X    
  295. X    if(!to_delete && !to_store && !num_chgd_status && !resyncing) {
  296. X      dprint(3, (debugfile, "Folder keep as is!\n"));
  297. X      error("Folder unchanged.");
  298. X      return(0);
  299. X    }
  300. X
  301. X    /** we have to check to see what the sorting order was...so that
  302. X        the order in which we write messages is the same as the order
  303. X        of the messages originally.
  304. X        We only need to do this if there are any messages to be
  305. X        written out (either to keep or to store). **/
  306. X
  307. X    if ((to_keep || to_store ) && sortby != MAILBOX_ORDER) {
  308. X      last_sortby = sortby;
  309. X      sortby = MAILBOX_ORDER;
  310. X      sort_mailbox(message_count, FALSE);
  311. X      sortby = last_sortby;
  312. X    }
  313. X
  314. X    /* Formulate message as to number of keeps, stores, and deletes.
  315. X     * This is only complex so that the message is good English.
  316. X     */
  317. X    if (to_keep > 0) {
  318. X      if (to_store > 0) {
  319. X        if (to_delete > 0)
  320. X          sprintf(buffer,
  321. X                "[Keeping %d message%s, storing %d, and deleting %d.]", 
  322. X            to_keep, plural(to_keep), to_store, to_delete);
  323. X        else
  324. X          sprintf(buffer, "[Keeping %d message%s and storing %d.]", 
  325. X            to_keep, plural(to_keep), to_store);
  326. X      } else {
  327. X        if (to_delete > 0)
  328. X          sprintf(buffer, "[Keeping %d message%s and deleting %d.]", 
  329. X            to_keep, plural(to_keep), to_delete);
  330. X        else
  331. X          sprintf(buffer, "[Keeping %s.]",
  332. X            to_keep > 1 ? "all messages" : "message");
  333. X      }
  334. X    } else if (to_store > 0) {
  335. X      if (to_delete > 0)
  336. X        sprintf(buffer, "[Storing %d message%s and deleting %d.]", 
  337. X          to_store, plural(to_store), to_delete);
  338. X      else 
  339. X        sprintf(buffer, "[Storing %s.]",
  340. X          to_store > 1? "all messages" : "message");
  341. X
  342. X    } else {
  343. X      if (to_delete > 0)
  344. X        sprintf(buffer, "[Deleting all messages.]");
  345. X      else
  346. X        buffer[0] = '\0';
  347. X    }
  348. X    /* NOTE: don't use variable "buffer" till message is output later */
  349. X
  350. X    /** next, let's lock the file up and make one last size check **/
  351. X
  352. X    if (folder_type == SPOOL)
  353. X      lock(OUTGOING);
  354. X    
  355. X    if (mailfile_size != bytes(cur_folder)) {
  356. X      unlock();
  357. X      error("New mail has just arrived. Resynchronizing...");
  358. X      return(-1);
  359. X    }
  360. X    
  361. X    /* Everything's GO - so ouput that user message and go to it. */
  362. X
  363. X    dprint(2, (debugfile, "Action: %s\n", buffer));
  364. X    error(buffer);
  365. X
  366. X    /* Store messages slated for storage in received mail folder */
  367. X    if (to_store > 0) {
  368. X      if ((errno = can_open(recvd_mail, "a"))) {
  369. X        error1(
  370. X          "Permission to append to %s denied!  Leaving folder intact.\n",
  371. X          recvd_mail);
  372. X        dprint(1, (debugfile,
  373. X          "Error: Permission to append to folder %s denied!! (%s)\n",
  374. X          recvd_mail, "leavembox"));
  375. X        dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  376. X          error_description(errno)));
  377. X        unlock();
  378. X        return(0);
  379. X      }
  380. X      if ((temp = fopen(recvd_mail,"a")) == NULL) {
  381. X        unlock();
  382. X        dprint(1, (debugfile, "Error: could not append to file %s\n", 
  383. X          recvd_mail));
  384. X        dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  385. X          error_description(errno)));
  386. X        sprintf(buffer, "Could not append to folder %s!", recvd_mail);
  387. X        Centerline(LINES-1, buffer);
  388. X        emergency_exit();
  389. X      }
  390. X      dprint(2, (debugfile, "Storing message%s ", plural(to_store)));
  391. X      for (i = 0; i < message_count; i++) {
  392. X        if(headers[i]->exit_disposition == STORE) {
  393. X          current = i+1;
  394. X          dprint(2, (debugfile, "#%d, ", current));
  395. X          copy_message("", temp, FALSE, FALSE, TRUE);
  396. X        }
  397. X      }
  398. X      fclose(temp);
  399. X      dprint(2, (debugfile, "\n\n"));
  400. X      chown(recvd_mail, userid, groupid);
  401. X    }
  402. X
  403. X    /* If there are any messages to keep, first copy them to a
  404. X     * temp file, then remove original and copy whole temp file over.
  405. X     */
  406. X    if (to_keep > 0) {
  407. X      sprintf(temp_keep_file, "%s%d", temp_file, getpid());
  408. X      if ((errno = can_open(temp_keep_file, "w"))) {
  409. X        error1(
  410. X"Permission to create temp file %s for writing denied! Leaving folder intact.",
  411. X          temp_keep_file);
  412. X        dprint(1, (debugfile,
  413. X          "Error: Permission to create temp file %s denied!! (%s)\n",
  414. X          temp_keep_file, "leavembox"));
  415. X        dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  416. X          error_description(errno)));
  417. X        unlock();
  418. X        return(0);
  419. X      }
  420. X      if ((temp = fopen(temp_keep_file,"w")) == NULL) {
  421. X        unlock();
  422. X        dprint(1, (debugfile, "Error: could not create file %s\n", 
  423. X          temp_keep_file));
  424. X        dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  425. X          error_description(errno)));
  426. X        sprintf(buffer, "Could not create temp file %s!", temp_keep_file);
  427. X        Centerline(LINES-1, buffer);
  428. X        emergency_exit();
  429. X      }
  430. X      dprint(2, (debugfile, "Copying to temp file message%s to be kept ",
  431. X        plural(to_keep)));
  432. X      for (i = 0; i < message_count; i++) {
  433. X        if(headers[i]->exit_disposition == KEEP) {
  434. X          current = i+1;
  435. X          dprint(2, (debugfile, "#%d, ", current));
  436. X          copy_message("", temp, FALSE, FALSE, TRUE);
  437. X        }
  438. X      }
  439. X      fclose(temp);
  440. X      dprint(2, (debugfile, "\n\n"));
  441. X
  442. X    } else if (folder_type == NON_SPOOL && !keep_empty_files) {
  443. X
  444. X      /* i.e. if no messages were to be kept and this is not a spool
  445. X       * folder and we aren't keeping empty non-spool folders,
  446. X       * simply remove the old original folder and that's it!
  447. X       */
  448. X      (void)unlink(cur_folder);
  449. X      return(1);
  450. X    }
  451. X
  452. X    /* Otherwise we have some work left to do! */
  453. X
  454. X    /* Get original permissions and access time of the original
  455. X     * mail folder before we remove it.
  456. X     */
  457. X    if(save_file_stats(cur_folder) != 0) {
  458. X      error1("Problems saving permissions of folder %s!", cur_folder);
  459. X      sleep(2);
  460. X    }
  461. X      
  462. X        if (stat(cur_folder, &buf) != 0) {
  463. X      dprint(1, (debugfile, "Error: errno %s attempting to stat file %s\n", 
  464. X             error_name(errno), cur_folder));
  465. X          error3("Error %s (%s) on stat(%s).", error_name(errno), 
  466. X        error_description(errno), cur_folder);
  467. X    }
  468. X
  469. X    /* Close and remove the original folder.
  470. X     * However, if we are going to copy a temp file of kept messages
  471. X     * to it, and this is a locked (spool) mailbox, we need to keep
  472. X     * it locked during this process. Unfortunately,
  473. X     * if we did our LOCK_BY_FLOCK, unlinking the original will kill the
  474. X     * lock, so we have to resort to copying the temp file to the original
  475. X     * file while keeping the original open.
  476. X     */
  477. X
  478. X    fclose(mailfile);
  479. X
  480. X    if(to_keep) {
  481. X#ifdef LOCK_BY_FLOCK
  482. X      need_to_copy = (folder_type == SPOOL ? TRUE : FALSE);
  483. X#else
  484. X      need_to_copy = FALSE;
  485. X#endif
  486. X      if(!need_to_copy) {
  487. X        unlink(cur_folder);
  488. X        if (link(temp_keep_file, cur_folder) != 0) {
  489. X          if(errno == EXDEV || errno == EEXIST) {
  490. X        /* oops - can't link across file systems - use copy instead */
  491. X        need_to_copy = TRUE;
  492. X          } else {
  493. X        dprint(1, (debugfile, "link(%s, %s) failed (leavembox)\n", 
  494. X               temp_keep_file, cur_folder));
  495. X        dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  496. X              error_description(errno)));
  497. X        error2("Link failed! %s - %s.", error_name(errno),
  498. X          error_description(errno));
  499. X        unlock();
  500. X        emergency_exit();
  501. X          }
  502. X        }
  503. X      }
  504. X
  505. X      if(need_to_copy) {
  506. X
  507. X        if (copy(temp_keep_file, cur_folder) != 0) {
  508. X
  509. X          /* copy to cur_folder failed - try to copy to special file */
  510. X          dprint(1, (debugfile, "leavembox: copy(%s, %s) failed;",
  511. X              temp_keep_file, cur_folder));
  512. X          dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  513. X           error_description(errno)));
  514. X          error("Couldn't modify folder!");
  515. X          sleep(1);
  516. X          sprintf(cur_folder,"%s/%s", home, unedited_mail);
  517. X          if (copy(temp_keep_file, cur_folder) != 0) {
  518. X
  519. X        /* couldn't copy to special file either */
  520. X        dprint(1, (debugfile, 
  521. X            "leavembox: couldn't copy to %s either!!  Help;", 
  522. X            cur_folder));
  523. X        dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  524. X            error_description(errno)));
  525. X        error("Something godawful is happening to me!!!");
  526. X        unlock();
  527. X        emergency_exit();
  528. X          } else {
  529. X        dprint(1, (debugfile,
  530. X            "\nWoah! Confused - Saved mail in %s (leavembox)\n", 
  531. X            cur_folder));
  532. X        error1("Saved mail in %s.", cur_folder);
  533. X        sleep(1);
  534. X          }
  535. X        }
  536. X      }
  537. X
  538. X      /* link or copy complete - remove temp keep file */
  539. X      unlink(temp_keep_file);
  540. X
  541. X    } else if(folder_type == SPOOL || keep_empty_files) {
  542. X
  543. X      /* if this is an empty spool file, or if this is an empty non spool 
  544. X       * file and we keep empty non spool files (we always keep empty
  545. X       * spool files), create an empty file */
  546. X
  547. X      if(folder_type == NON_SPOOL)
  548. X        error1("Keeping empty folder '%s'.", cur_folder);
  549. X      temp = fopen(cur_folder, "w");
  550. X      fclose(temp);
  551. X    }
  552. X
  553. X    /* restore permissions and access times of folder */
  554. X
  555. X    if(restore_file_stats(cur_folder) != 1) {
  556. X      error1("Problems restoring permissions of folder %s!", cur_folder);
  557. X      sleep(2);
  558. X    }
  559. X
  560. X#ifdef BSD
  561. X    utime_buffer[0]     = buf.st_atime;
  562. X    utime_buffer[1]     = buf.st_mtime;
  563. X#else
  564. X    utime_buffer.actime = buf.st_atime;
  565. X    utime_buffer.modtime= buf.st_mtime;
  566. X#endif
  567. X
  568. X#ifdef BSD
  569. X    if (utime(cur_folder, utime_buffer) != 0) {
  570. X#else
  571. X    if (utime(cur_folder, &utime_buffer) != 0) {
  572. X#endif
  573. X      dprint(1, (debugfile, 
  574. X         "Error: encountered error doing utime (leavmbox)\n"));
  575. X      dprint(1, (debugfile, "** %s - %s **\n", error_name(errno), 
  576. X           error_description(errno)));
  577. X      error2("Error %s trying to change file %s access time.", 
  578. X           error_name(errno), cur_folder);
  579. X    }
  580. X
  581. X
  582. X    mailfile_size = bytes(cur_folder);
  583. X    unlock();    /* remove the lock on the file ASAP! */
  584. X
  585. X    return(1);    
  586. X}
  587. X
  588. Xstatic int  lock_state = OFF;
  589. X
  590. Xstatic char lock_name[SLEN];
  591. X
  592. Xchar *
  593. Xmk_lockname(file_to_lock)
  594. Xchar *file_to_lock;
  595. X{
  596. X    /** Create the proper name of the lock file for file_to_lock,
  597. X        which is presumed to be a spool file full path (see
  598. X        get_folder_type()), and put it in the static area lock_name.
  599. X        Return lock_name for informational purposes.
  600. X     **/
  601. X
  602. X#ifdef XENIX
  603. X    /* lock is /tmp/[basename of file_to_lock].mlk */
  604. X    sprintf(lock_name, "/tmp/%.10s.mlk", strrchr(file_to_lock, '/')+1);
  605. X#else
  606. X    /* lock is [file_to_lock].lock */
  607. X    sprintf(lock_name, "%s.lock", file_to_lock);
  608. X#endif
  609. X    return(lock_name);
  610. X}
  611. X
  612. X
  613. Xstatic int flock_fd,    /* file descriptor for flocking mailbox itself */
  614. X       create_fd;    /* file descriptor for creating lock file */
  615. X
  616. Xlock(direction)
  617. Xint direction;
  618. X{
  619. X      /** Create lock file to ensure that we don't get any mail 
  620. X      while altering the folder contents!
  621. X      If it already exists sit and spin until 
  622. X         either the lock file is removed...indicating new mail
  623. X      or
  624. X         we have iterated MAX_ATTEMPTS times, in which case we
  625. X         either fail or remove it and make our own (determined
  626. X         by if REMOVE_AT_LAST is defined in header file
  627. X
  628. X      If direction == INCOMING then DON'T remove the lock file
  629. X      on the way out!  (It'd mess up whatever created it!).
  630. X
  631. X      But if that succeeds and if we are also locking by flock(),
  632. X      follow a similar algorithm. Now if we can't lock by flock(),
  633. X      we DO need to remove the lock file, since if we got this far,
  634. X      we DID create it, not another process.
  635. X      **/
  636. X
  637. X      register int create_iteration = 0,
  638. X           flock_iteration = 0;
  639. X
  640. X      /* formulate lock file name */
  641. X      mk_lockname(cur_folder);
  642. X
  643. X      /* try to assert create lock file MAX_ATTEMPTS times */
  644. X      do {
  645. X
  646. X    errno = 0;
  647. X    if((create_fd=open(lock_name,O_WRONLY | O_CREAT | O_EXCL,0777)) != -1)
  648. X      break;
  649. X    else {
  650. X      if(errno != EEXIST) {
  651. X        /* Creation of lock failed NOT because it already exists!!! */
  652. X
  653. X        if (direction == OUTGOING) {
  654. X          dprint(1, (debugfile, 
  655. X        "Error encountered attempting to create lock %s\n", lock_name));
  656. X          dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  657. X            error_description(errno)));
  658. X          MoveCursor(LINES, 0);
  659. X          printf(
  660. X       "\n\rError encountered while attempting to create lock file %s;\n\r",
  661. X        lock_name);
  662. X          printf("** %s - %s.**\n\r\n\r",
  663. X        error_name(errno), error_description(errno));
  664. X        } else {    /* incoming - permission denied in the middle?  Odd. */
  665. X          dprint(1, (debugfile,
  666. X           "Can't create lock file: creat(%s) raises error %s (lock)\n", 
  667. X        lock_name, error_name(errno)));
  668. X          error1(
  669. X           "Can't create lock file! Need write permission in \"%s\".\n\r",
  670. X        mailhome);
  671. X        }
  672. X        leave();
  673. X      }
  674. X    }
  675. X    dprint(2, (debugfile,"File '%s' already exists!  Waiting...(lock)\n", 
  676. X      lock_name));
  677. X    error1(
  678. X      "Waiting to read mailbox while mail is being received: attempt #%d",
  679. X      create_iteration);
  680. X    sleep(5);
  681. X      } while (create_iteration++ < MAX_ATTEMPTS);
  682. X      clear_error();
  683. X
  684. X      if(errno != 0) {
  685. X    
  686. X    /* we weren't able to create the lock file */
  687. X
  688. X#ifdef REMOVE_AT_LAST
  689. X
  690. X    /** time to waste the lock file!  Must be there in error! **/
  691. X    dprint(2, (debugfile, 
  692. X       "Warning: I'm giving up waiting - removing lock file(lock)\n"));
  693. X    if (direction == INCOMING)
  694. X      PutLine0(LINES, 0,"\nTimed out - removing current lock file...");
  695. X    else
  696. X      error("Throwing away the current lock file!");
  697. X
  698. X    if (unlink(lock_name) != 0) {
  699. X      dprint(1, (debugfile,
  700. X        "Error %s (%s)\n\ttrying to unlink file %s (%s)\n", 
  701. X        error_name(errno), error_description(errno), lock_name, "lock"));
  702. X      PutLine1(LINES, 0, 
  703. X        "\n\rCouldn't remove the current lock file %s\n\r", lock_name);
  704. X      PutLine2(LINES, 0, "** %s - %s **\n\r", error_name(errno),
  705. X        error_description(errno));
  706. X      if (direction == INCOMING)
  707. X        leave();
  708. X      else
  709. X        emergency_exit();
  710. X    }
  711. X
  712. X    /* we've removed the bad lock, let's try to assert lock once more */
  713. X    if((create_fd=open(lock_name,O_WRONLY | O_CREAT | O_EXCL,0777)) == -1){
  714. X
  715. X      /* still can't lock it - just give up */
  716. X      dprint(1, (debugfile, 
  717. X        "Error encountered attempting to create lock %s\n", lock_name));
  718. X      dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  719. X        error_description(errno)));
  720. X      MoveCursor(LINES, 0);
  721. X      printf(
  722. X      "\n\rError encountered while attempting to create lock file %s;\n\r",
  723. X        lock_name);
  724. X      printf("** %s - %s.**\n\r\n\r", error_name(errno),
  725. X        error_description(errno));
  726. X      leave();
  727. X    }
  728. X#else
  729. X    /* Okay...we die and leave, not updating the mailfile mbox or
  730. X       any of those! */
  731. X
  732. X    if (direction == INCOMING) {
  733. X      PutLine1(LINES, 0, "\n\r\n\rGiving up after %d iterations.\n\r", 
  734. X        create_iteration);
  735. X      PutLine0(LINES, 0, 
  736. X      "\n\rPlease try to read your mail again in a few minutes.\n\r\n\r");
  737. X      dprint(1, (debugfile, 
  738. X        "Warning: bailing out after %d iterations...(lock)\n",
  739. X        create_iteration));
  740. X      leave_locked(0);
  741. X    } else {
  742. X      dprint(1, (debugfile, 
  743. X       "Warning: after %d iterations, timed out! (lock)\n",
  744. X       create_iteration));
  745. X      leave(error("Timed out on locking mailbox.  Leaving program."));
  746. X    }
  747. X#endif
  748. X      }
  749. X
  750. X      /* If we're here we successfully created the lock file */
  751. X      dprint(5,
  752. X    (debugfile, "Lock %s %s for file %s on.\n", lock_name,
  753. X    (direction == INCOMING ? "incoming" : "outgoing"), cur_folder));
  754. X
  755. X      (void)close(create_fd);
  756. X
  757. X#ifdef LOCK_BY_FLOCK
  758. X      /* Now we also need to lock the file with flock(2) */
  759. X
  760. X      /* Open mail file separately for locking */
  761. X      if((flock_fd = open(cur_folder, O_RDONLY)) < 0) {
  762. X    dprint(1, (debugfile, 
  763. X        "Error encountered attempting to reopen %s for lock\n", cur_folder));
  764. X    dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  765. X        error_description(errno)));
  766. X    MoveCursor(LINES, 0);
  767. X    printf(
  768. X "\n\rError encountered while attempting to reopen mailbox %s for lock;\n\r", 
  769. X          cur_folder);
  770. X    printf("** %s - %s.**\n\r\n\r", error_name(errno),
  771. X          error_description(errno));
  772. X    (void)unlink(lock_name);
  773. X    leave();
  774. X      }
  775. X
  776. X      /* try to assert lock MAX_ATTEMPTS times */
  777. X      do {
  778. X
  779. X    errno = 0;
  780. X    if(flock(flock_fd, LOCK_NB | LOCK_EX) != -1)
  781. X      break;
  782. X    else {
  783. X      if(errno != EWOULDBLOCK) {
  784. X
  785. X        /* Creation of lock failed NOT because it already exists!!! */
  786. X
  787. X        dprint(1, (debugfile, 
  788. X          "Error encountered attempting to flock %s\n", cur_folder));
  789. X        dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  790. X          error_description(errno)));
  791. X        MoveCursor(LINES, 0);
  792. X        printf(
  793. X     "\n\rError encountered while attempting to flock mailbox %s;\n\r", 
  794. X          cur_folder);
  795. X        printf("** %s - %s.**\n\r\n\r", error_name(errno),
  796. X          error_description(errno));
  797. X        (void)unlink(lock_name);
  798. X        leave();
  799. X      }
  800. X    }
  801. X    dprint(2, (debugfile,
  802. X      "Mailbox '%s' already locked!  Waiting...(lock)\n", cur_folder));
  803. X    error1(
  804. X      "Waiting to read mailbox while mail is being received: attempt #%d",
  805. X      flock_iteration);
  806. X    sleep(5);
  807. X      } while (flock_iteration++ < MAX_ATTEMPTS);
  808. X      clear_error();
  809. X
  810. X      if(errno != 0) {
  811. X
  812. X    /* We couldn't lock the file. We die and leave not updating
  813. X     * the mailfile mbox or any of those! */
  814. X
  815. X    if (direction == INCOMING) {
  816. X      PutLine1(LINES, 0, "\n\r\n\rGiving up after %d iterations.\n\r", 
  817. X        flock_iteration);
  818. X      PutLine0(LINES, 0, 
  819. X      "\n\rPlease try to read your mail again in a few minutes.\n\r\n\r");
  820. X      dprint(1, (debugfile, 
  821. X        "Warning: bailing out after %d iterations...(lock)\n",
  822. X        flock_iteration));
  823. X    } else {
  824. X      dprint(1, (debugfile, 
  825. X       "Warning: after %d iterations, timed out! (lock)\n",
  826. X       flock_iteration));
  827. X    }
  828. X    (void)unlink(lock_name);
  829. X    leave(error("Timed out on locking mailbox. Leaving program."));
  830. X      }
  831. X
  832. X      /* We locked the file */
  833. X      dprint(5,
  834. X    (debugfile, "Lock %s on file %s on.\n",
  835. X    (direction == INCOMING ? "incoming" : "outgoing"), cur_folder));
  836. X#endif
  837. X
  838. X      dprint(5,
  839. X    (debugfile, "Lock %s for file %s on successfully.\n",
  840. X    (direction == INCOMING ? "incoming" : "outgoing"), cur_folder));
  841. X      lock_state = ON;
  842. X      return(0);
  843. X}
  844. X
  845. Xint
  846. Xunlock()
  847. X{
  848. X    /** Remove the lock file!    This must be part of the interrupt
  849. X        processing routine to ensure that the lock file is NEVER
  850. X        left sitting in the mailhome directory!
  851. X
  852. X        If also using flock(), remove the file lock as well.
  853. X     **/
  854. X
  855. X    int retcode = 0;
  856. X
  857. X    dprint(5,
  858. X      (debugfile, "Lock %s for file %s %s off.\n",
  859. X        (*lock_name ? lock_name : "none"), cur_folder,
  860. X        (lock_state == ON ? "going" : "already")));
  861. X
  862. X    if(lock_state == ON) {
  863. X
  864. X#ifdef LOCK_BY_FLOCK
  865. X      if((retcode = flock(flock_fd, LOCK_UN)) == -1) {
  866. X        dprint(1, (debugfile,
  867. X          "Error %s (%s)\n\ttrying to unlock file %s (%s)\n", 
  868. X          error_name(errno), error_description(errno), cur_folder, "unlock"));
  869. X
  870. X        /* try to force unlock by closing file */
  871. X        if(close(flock_fd) == -1) {
  872. X          dprint(1, (debugfile,
  873. X      "Error %s (%s)\n\ttrying to force unlock file %s via close() (%s)\n", 
  874. X          error_name(errno), error_description(errno), cur_folder, "unlock"));
  875. X          error1("Couldn't unlock my own mailbox %s!", cur_folder);
  876. X          return(retcode);
  877. X        }
  878. X      }
  879. X      (void)close(flock_fd);
  880. X#endif
  881. X      if((retcode = unlink(lock_name)) == 0) {    /* remove lock file */
  882. X        *lock_name = '\0';        /* null lock file name */
  883. X        lock_state = OFF;        /* indicate we don't have a lock on */
  884. X      } else {
  885. X        dprint(1, (debugfile,
  886. X          "Error %s (%s)\n\ttrying to unlink file %s (%s)\n", 
  887. X          error_name(errno), error_description(errno), lock_name,"unlock"));
  888. X          error1("Couldn't remove my own lock file %s!", lock_name);
  889. X      }
  890. X    }
  891. X    return(retcode);
  892. X}
  893. SHAR_EOF
  894. echo "File src/leavembox.c is complete"
  895. chmod 0444 src/leavembox.c || echo "restore of src/leavembox.c fails"
  896. echo "x - extracting src/limit.c (Text)"
  897. sed 's/^X//' << 'SHAR_EOF' > src/limit.c &&
  898. X
  899. Xstatic char rcsid[] = "@(#)$Id: limit.c,v 2.8 89/03/25 21:46:36 syd Exp $";
  900. X
  901. X/*******************************************************************************
  902. X *  The Elm Mail System  -  $Revision: 2.8 $   $State: Exp $
  903. X *
  904. X *             Copyright (c) 1986, 1987 Dave Taylor
  905. X *             Copyright (c) 1988, 1989 USENET Community Trust
  906. X *******************************************************************************
  907. X * Bug reports, patches, comments, suggestions should be sent to:
  908. X *
  909. X *    Syd Weinstein, Elm Coordinator
  910. X *    elm@dsinc.UUCP            dsinc!elm
  911. X *
  912. X *******************************************************************************
  913. X * $Log:    limit.c,v $
  914. X * Revision 2.8  89/03/25  21:46:36  syd
  915. X * Initial 2.2 Release checkin
  916. X * 
  917. X *
  918. X ******************************************************************************/
  919. X
  920. X/** This stuff is inspired by MH and dmail and is used to 'select'
  921. X    a subset of the existing mail in the folder based on one of a
  922. X    number of criteria.  The basic tricks are pretty easy - we have
  923. X    as status of VISIBLE associated with each header stored in the
  924. X    (er) mind of the computer (!) and simply modify the commands to
  925. X    check that flag...the global variable `selected' is set to the
  926. X    number of messages currently selected, or ZERO if no select.
  927. X**/
  928. X
  929. X#include "headers.h"
  930. X
  931. X#define TO        1
  932. X#define FROM        2
  933. X
  934. Xchar *shift_lower();
  935. X
  936. Xint
  937. Xlimit()
  938. X{
  939. X    /** returns non-zero if we changed selection criteria = need redraw **/
  940. X    
  941. X    char criteria[STRING], first[STRING], rest[STRING], msg[STRING];
  942. X    static char prompt[] = "Enter criteria or '?' for help: ";
  943. X    int  last_selected, all;
  944. X
  945. X    last_selected = selected;
  946. X    all = 0;
  947. X
  948. X    if (selected) {
  949. X      PutLine1(LINES-2, 0, 
  950. X        "Already have selection criteria - add more? (y/n) n%c",
  951. X        BACKSPACE);
  952. X      criteria[0] = ReadCh();
  953. X      if (tolower(criteria[0]) == 'y') {
  954. X        Write_to_screen("Yes.", 0);
  955. X        PutLine0(LINES-3, COLUMNS-30, "Adding criteria...");
  956. X      } else {
  957. X        Write_to_screen("No.", 0);
  958. X        selected = 0;
  959. X        PutLine0(LINES-3, COLUMNS-30, "Change criteria...");
  960. X      }
  961. X    }
  962. X
  963. X    while(1) {
  964. X      PutLine1(LINES-2, 0, prompt);
  965. X      CleartoEOLN();
  966. X
  967. X      criteria[0] = '\0';
  968. X      optionally_enter(criteria, LINES-2, strlen(prompt), FALSE, FALSE);
  969. X      error("");
  970. X      
  971. X      if (strlen(criteria) == 0) {
  972. X        /* no change */
  973. X        selected = last_selected;
  974. X        return(FALSE);    
  975. X      }
  976. X
  977. X      split_word(criteria, first, rest);
  978. X
  979. X      if (equal(first, "?")) {
  980. X         if(last_selected)
  981. X           error(
  982. X              "Enter: {\"subject\",\"to\",\"from\"} [pattern] OR \"all\"");
  983. X         else
  984. X           error("Enter: {\"subject\",\"to\",\"from\"} [pattern]");
  985. X         continue;
  986. X      } else if (equal(first, "all")) {
  987. X        all++;
  988. X        selected = 0;
  989. X      }
  990. X      else if (equal(first, "subj") || equal(first, "subject"))
  991. X        selected = limit_selection(SUBJECT, rest, selected);
  992. X      else if (equal(first, "to"))
  993. X        selected = limit_selection(TO, rest, selected);
  994. X      else if (equal(first, "from"))
  995. X        selected = limit_selection(FROM, rest, selected);
  996. X      else {
  997. X        error1("\"%s\" not a valid criterion.", first);
  998. X        continue;
  999. X      }
  1000. X      break;
  1001. X    }
  1002. X
  1003. X    if(all && last_selected)
  1004. X      strcpy(msg, "Returned to unlimited display.");
  1005. X    else if(selected)
  1006. X      sprintf(msg, "%d message%s selected.", selected, plural(selected));
  1007. X    else
  1008. X      strcpy(msg, "No messages selected.");
  1009. X    set_error(msg);
  1010. X
  1011. X    /* we need a redraw if there had been a selection or there is now. */
  1012. X    if(last_selected || selected) {
  1013. X      /* if current message won't be on new display, go to first message */
  1014. X      if(selected && !(headers[current-1]->status & VISIBLE))
  1015. X        current = visible_to_index(1)+1;
  1016. X      return(TRUE);
  1017. X    } else {
  1018. X      return(FALSE);
  1019. X    }
  1020. X}
  1021. X
  1022. Xint
  1023. Xlimit_selection(based_on, pattern, additional_criteria)
  1024. Xint based_on, additional_criteria;
  1025. Xchar *pattern;
  1026. X{
  1027. X    /** Given the type of criteria, and the pattern, mark all
  1028. X        non-matching headers as ! VISIBLE.  If additional_criteria,
  1029. X        don't mark as visible something that isn't currently!
  1030. X    **/
  1031. X
  1032. X    register int iindex, count = 0;
  1033. X
  1034. X    dprint(2, (debugfile, "\n\n\n**limit on %d - '%s' - (%s) **\n\n",
  1035. X           based_on, pattern, additional_criteria?"add'tl":"base"));
  1036. X
  1037. X    if (based_on == SUBJECT) {
  1038. X      for (iindex = 0; iindex < message_count; iindex++)
  1039. X        if (! in_string(shift_lower(headers[iindex]->subject), pattern))
  1040. X          headers[iindex]->status &= ~VISIBLE;
  1041. X        else if (additional_criteria &&     
  1042. X             !(headers[iindex]->status & VISIBLE))
  1043. X          headers[iindex]->status &= ~VISIBLE;    /* shut down! */
  1044. X        else { /* mark it as readable */
  1045. X          headers[iindex]->status |= VISIBLE;
  1046. X          count++;
  1047. X          dprint(5, (debugfile,
  1048. X             "  Message %d (%s from %s) marked as visible\n",
  1049. X            iindex, headers[iindex]->subject,
  1050. X            headers[iindex]->from));
  1051. X        }
  1052. X    }
  1053. X    else if (based_on == FROM) {
  1054. X      for (iindex = 0; iindex < message_count; iindex++)
  1055. X        if (! in_string(shift_lower(headers[iindex]->from), pattern))
  1056. X          headers[iindex]->status &= ~VISIBLE;
  1057. X        else if (additional_criteria &&     
  1058. X             !(headers[iindex]->status & VISIBLE))
  1059. X          headers[iindex]->status &= ~VISIBLE;    /* shut down! */
  1060. X        else { /* mark it as readable */
  1061. X          headers[iindex]->status |= VISIBLE;
  1062. X          count++;
  1063. X          dprint(5, (debugfile, 
  1064. X            "  Message %d (%s from %s) marked as visible\n",
  1065. X            iindex, headers[iindex]->subject,
  1066. X            headers[iindex]->from));
  1067. X        }
  1068. X    }
  1069. X    else if (based_on == TO) {
  1070. X      for (iindex = 0; iindex < message_count; iindex++)
  1071. X        if (! in_string(shift_lower(headers[iindex]->to), pattern))
  1072. X          headers[iindex]->status &= ~VISIBLE;
  1073. X        else if (additional_criteria &&     
  1074. X             !(headers[iindex]->status & VISIBLE))
  1075. X          headers[iindex]->status &= ~VISIBLE;    /* shut down! */
  1076. X        else { /* mark it as readable */
  1077. X          headers[iindex]->status |= VISIBLE;
  1078. X          count++;
  1079. X          dprint(5, (debugfile,
  1080. X            "  Message %d (%s from %s) marked as visible\n",
  1081. X            iindex, headers[iindex]->subject,
  1082. X            headers[iindex]->from));
  1083. X        }
  1084. X    }
  1085. X
  1086. X    dprint(4, (debugfile, "\n** returning %d selected **\n\n\n", count));
  1087. X
  1088. X    return(count);
  1089. X}
  1090. X
  1091. Xint
  1092. Xnext_message(iindex, skipdel)
  1093. Xregister int iindex, skipdel;
  1094. X{
  1095. X    /** Given 'iindex', this routine will return the actual iindex into the
  1096. X        array of the NEXT message, or '-1' iindex is the last.
  1097. X        If skipdel, return the iindex for the NEXT undeleted message.
  1098. X        If selected, return the iindex for the NEXT message marked VISIBLE.
  1099. X    **/
  1100. X
  1101. X    register int remember_for_debug;
  1102. X
  1103. X    if(iindex < 0) return(-1);    /* invalid argument value! */
  1104. X
  1105. X    remember_for_debug = iindex;
  1106. X
  1107. X    for(iindex++;iindex < message_count; iindex++)
  1108. X      if (((headers[iindex]->status & VISIBLE) || (!selected))
  1109. X        && (!(headers[iindex]->status & DELETED) || (!skipdel))) {
  1110. X          dprint(9, (debugfile, "[Next%s%s: given %d returning %d]\n", 
  1111. X          (skipdel ? " undeleted" : ""),
  1112. X          (selected ? " visible" : ""),
  1113. X          remember_for_debug+1, iindex+1));
  1114. X          return(iindex);
  1115. X      }
  1116. X    return(-1);
  1117. X}
  1118. X
  1119. Xint
  1120. Xprev_message(iindex, skipdel)
  1121. Xregister int iindex, skipdel;
  1122. X{
  1123. X    /** Like next_message, but the PREVIOUS message. **/
  1124. X
  1125. X    register int remember_for_debug;
  1126. X
  1127. X    if(iindex >= message_count) return(-1);    /* invalid argument value! */
  1128. X
  1129. X    remember_for_debug = iindex;
  1130. X    for(iindex--; iindex >= 0; iindex--)
  1131. X      if (((headers[iindex]->status & VISIBLE) || (!selected))
  1132. X        && (!(headers[iindex]->status & DELETED) || (!skipdel))) {
  1133. X          dprint(9, (debugfile, "[Previous%s%s: given %d returning %d]\n", 
  1134. X          (skipdel ? " undeleted" : ""),
  1135. X          (selected ? " visible" : ""),
  1136. X          remember_for_debug+1, iindex+1));
  1137. X          return(iindex);
  1138. X      }
  1139. X    return(-1);
  1140. X}
  1141. X
  1142. X
  1143. Xint
  1144. Xcompute_visible(message)
  1145. Xint message;
  1146. X{
  1147. X    /** return the 'virtual' iindex of the specified message in the
  1148. X        set of messages - that is, if we have the 25th message as
  1149. X        the current one, but it's #2 based on our limit criteria,
  1150. X        this routine, given 25, will return 2.
  1151. X    **/
  1152. X
  1153. X    register int iindex, count = 0;
  1154. X
  1155. X    if (! selected) return(message);
  1156. X
  1157. X    if (message < 1) message = 1;    /* normalize */
  1158. X
  1159. X    for (iindex = 0; iindex < message; iindex++)
  1160. X       if (headers[iindex]->status & VISIBLE) 
  1161. X         count++;
  1162. X
  1163. X    dprint(4, (debugfile,
  1164. X        "[compute-visible: displayed message %d is actually %d]\n",
  1165. X        count, message));
  1166. X
  1167. X    return(count);
  1168. X}
  1169. X
  1170. Xint
  1171. Xvisible_to_index(message)
  1172. Xint message;
  1173. X{
  1174. X    /** Given a 'virtual' iindex, return a real one.  This is the
  1175. X        flip-side of the routine above, and returns (message_count+1)
  1176. X        if it cannot map the virtual iindex requested (too big) 
  1177. X    **/
  1178. X
  1179. X    register int iindex = 0, count = 0;
  1180. X
  1181. X    for (iindex = 0; iindex < message_count; iindex++) {
  1182. X       if (headers[iindex]->status & VISIBLE) 
  1183. X         count++;
  1184. X       if (count == message) {
  1185. X         dprint(4, (debugfile,
  1186. X             "visible-to-index: (up) index %d is displayed as %d\n",
  1187. X             message, iindex));
  1188. X         return(iindex);
  1189. X       }
  1190. X    }
  1191. X
  1192. X    dprint(4, (debugfile, "index %d is NOT displayed!\n", message));
  1193. X
  1194. X    return(message_count+1);
  1195. X}
  1196. SHAR_EOF
  1197. chmod 0444 src/limit.c || echo "restore of src/limit.c fails"
  1198. echo "x - extracting src/mailmsg1.c (Text)"
  1199. sed 's/^X//' << 'SHAR_EOF' > src/mailmsg1.c &&
  1200. X
  1201. Xstatic char rcsid[] = "@(#)$Id: mailmsg1.c,v 2.15 89/03/25 21:46:38 syd Exp $";
  1202. X
  1203. X/*******************************************************************************
  1204. X *  The Elm Mail System  -  $Revision: 2.15 $   $State: Exp $
  1205. X *
  1206. X *             Copyright (c) 1986, 1987 Dave Taylor
  1207. X *             Copyright (c) 1988, 1989 USENET Community Trust
  1208. X *******************************************************************************
  1209. X * Bug reports, patches, comments, suggestions should be sent to:
  1210. X *
  1211. X *    Syd Weinstein, Elm Coordinator
  1212. X *    elm@dsinc.UUCP            dsinc!elm
  1213. X *
  1214. X *******************************************************************************
  1215. X * $Log:    mailmsg1.c,v $
  1216. X * Revision 2.15  89/03/25  21:46:38  syd
  1217. X * Initial 2.2 Release checkin
  1218. X * 
  1219. X *
  1220. X ******************************************************************************/
  1221. X
  1222. X/** Interface to allow mail to be sent to users.  Part of ELM  **/
  1223. X
  1224. X
  1225. X#include "headers.h"
  1226. X
  1227. X/** strings defined for the hdrconfg routines **/
  1228. X
  1229. Xchar subject[SLEN], in_reply_to[SLEN], expires[SLEN], 
  1230. X     action[SLEN], priority[SLEN], reply_to[SLEN], to[VERY_LONG_STRING], 
  1231. X     cc[VERY_LONG_STRING], expanded_to[VERY_LONG_STRING], 
  1232. X     expanded_cc[VERY_LONG_STRING], user_defined_header[SLEN],
  1233. X     bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING];
  1234. X
  1235. Xchar *format_long(), *strip_commas(), *tail_of_string(), *strcpy();
  1236. Xunsigned long sleep();
  1237. X
  1238. Xint
  1239. Xsendmsg(given_to, given_cc, given_subject, edit_message, form_letter, replying)
  1240. Xchar *given_to, *given_cc, *given_subject;
  1241. Xint   edit_message, form_letter, replying;
  1242. X{
  1243. X    /** Prompt for fields and then call mail() to send the specified
  1244. X        message.  If 'edit_message' is true then don't allow the
  1245. X            message to be edited. 'form_letter' can be "YES" "NO" or "MAYBE".
  1246. X        if YES, then add the header.  If MAYBE, then add the M)ake form
  1247. X        option to the last question (see mailsg2.c) etc. etc. 
  1248. X        if (replying) then add an In-Reply-To: header...
  1249. X        Return TRUE if the main part of the screen has been changed
  1250. X        (useful for knowing whether a redraw is needed.
  1251. X    **/
  1252. X
  1253. X    int  copy_msg = FALSE, is_a_response = FALSE;
  1254. X
  1255. X    /* First: zero all current global message strings */
  1256. X
  1257. X    cc[0] = bcc[0] = reply_to[0] = expires[0] = '\0';
  1258. X    action[0] = priority[0] = user_defined_header[0] = in_reply_to[0] ='\0';
  1259. X    expanded_to[0] = expanded_cc[0] = expanded_bcc[0] = '\0';
  1260. X
  1261. X    strcpy(subject, given_subject);        /* copy given subject */
  1262. X    strcpy(to, given_to);            /* copy given to:     */
  1263. X    strcpy(cc, given_cc);            /*  and so on..       */
  1264. X
  1265. X    /******* And now the real stuff! *******/
  1266. X
  1267. X    copy_msg=copy_the_msg(&is_a_response); /* copy msg into edit buffer? */
  1268. X
  1269. X    if (get_to(to, expanded_to) == 0)   /* get the To: address and expand */
  1270. X      return(0);
  1271. X
  1272. X    /** if we're batchmailing, let's send it and GET OUTTA HERE! **/
  1273. X
  1274. X    if (batch_only) {
  1275. X      return(mail(FALSE, FALSE, form_letter));
  1276. X    }
  1277. X
  1278. X    display_to(expanded_to);    /* display the To: field on screen... */
  1279. X
  1280. X    dprint(3, (debugfile, "\nMailing to %s\n", expanded_to));
  1281. X  
  1282. X    if (get_subject(subject) == 0)        /* get the Subject: field */
  1283. X      return(0);
  1284. X
  1285. X    dprint(4, (debugfile, "Subject is %s\n", subject));
  1286. X
  1287. X    if (prompt_for_cc) {
  1288. X      if (get_copies(cc, expanded_to, expanded_cc, copy_msg) == 0)
  1289. X        return(0);
  1290. X
  1291. X      if (strlen(cc) > 0)
  1292. X        dprint(4, (debugfile, "Copies to %s\n", expanded_cc));
  1293. X    }
  1294. X
  1295. X    MoveCursor(LINES,0);    /* so you know you've hit <return> ! */
  1296. X
  1297. X    /** generate the In-Reply-To: header... **/
  1298. X
  1299. X    if (is_a_response && replying)
  1300. X      generate_reply_to(current-1);
  1301. X
  1302. X    /* and mail that puppy outta here! */
  1303. X    
  1304. X    return(mail(copy_msg, edit_message, form_letter));
  1305. X}
  1306. X
  1307. Xget_to(to_field, address)
  1308. Xchar *to_field, *address;
  1309. X{
  1310. X    /** prompt for the "To:" field, expanding into address if possible.
  1311. X        This routine returns ZERO if errored, or non-zero if okay **/
  1312. X
  1313. X    if (strlen(to_field) == 0) {
  1314. X      if (user_level < 2) {
  1315. X        PutLine0(LINES-2, 0, "Send the message to: ");
  1316. X        (void) optionally_enter(to_field, LINES-2, 21, FALSE, FALSE); 
  1317. X      }
  1318. X      else {
  1319. X        PutLine0(LINES-2, 0, "To: ");
  1320. X        (void) optionally_enter(to_field, LINES-2, 4, FALSE, FALSE); 
  1321. X      }
  1322. X      if (strlen(to_field) == 0) {
  1323. X        ClearLine(LINES-2);    
  1324. X        return(0);
  1325. X      }
  1326. X      (void) build_address(strip_commas(to_field), address); 
  1327. X    }
  1328. X    else if (mail_only) 
  1329. X      (void) build_address(strip_commas(to_field), address); 
  1330. X    else 
  1331. X      strcpy(address, to_field);
  1332. X    
  1333. X    if (strlen(address) == 0) {    /* bad address!  Removed!! */
  1334. X      ClearLine(LINES-2);
  1335. X      return(0);
  1336. X    }
  1337. X
  1338. X    return(1);        /* everything is okay... */
  1339. X}
  1340. X
  1341. Xget_subject(subject_field)
  1342. Xchar *subject_field;
  1343. X{
  1344. X    char    ch;
  1345. X
  1346. X    /** get the subject and return non-zero if all okay... **/
  1347. X    int len = 9, prompt_line;
  1348. X
  1349. X    prompt_line = mail_only ? 4 : LINES-2;
  1350. X
  1351. X    if (user_level == 0) {
  1352. X      PutLine0(prompt_line,0,"Subject of message: ");
  1353. X      len = 20;
  1354. X    }
  1355. X    else
  1356. X      PutLine0(prompt_line,0,"Subject: ");
  1357. X
  1358. X    CleartoEOLN();
  1359. X
  1360. X    if(optionally_enter(subject_field, prompt_line, len, TRUE, FALSE)==-1){
  1361. X      /** User hit the BREAK key! **/
  1362. X      MoveCursor(prompt_line,0);     
  1363. X      CleartoEOLN();
  1364. X      error("Mail not sent.");
  1365. X      return(0);
  1366. X    }
  1367. X
  1368. X    if (strlen(subject_field) == 0) {    /* zero length subject?? */
  1369. X      PutLine1(prompt_line,0,
  1370. X        "No subject - Continue with message? (y/n) n%c", BACKSPACE);
  1371. X
  1372. X      ch = ReadCh();
  1373. X      if (tolower(ch) != 'y') {    /* user says no! */
  1374. X        Write_to_screen("No.", 0);
  1375. X        ClearLine(prompt_line);
  1376. X        error("Mail not sent.");
  1377. X        return(0);
  1378. X      }
  1379. X      else {
  1380. X        Write_to_screen("Yes.", 0);
  1381. X        PutLine0(prompt_line,0,"Subject: <none>");
  1382. X        CleartoEOLN();
  1383. X      }
  1384. X    }
  1385. X
  1386. X    return(1);        /** everything is cruising along okay **/
  1387. X}
  1388. X
  1389. Xget_copies(cc_field, address, addressII, copy_message)
  1390. Xchar *cc_field, *address, *addressII;
  1391. Xint   copy_message;
  1392. X{
  1393. X    /** Get the list of people that should be cc'd, returning ZERO if
  1394. X        any problems arise.  Address and AddressII are for expanding
  1395. X        the aliases out after entry! 
  1396. X        If 'bounceback' is nonzero, add a cc to ourselves via the remote
  1397. X        site, but only if hops to machine are > bounceback threshold.
  1398. X        If copy-message, that means that we're going to have to invoke
  1399. X        a screen editor, so we'll need to delay after displaying the
  1400. X        possibly rewritten Cc: line...
  1401. X    **/
  1402. X    int prompt_line;
  1403. X
  1404. X    prompt_line = mail_only ? 5 : LINES - 1;
  1405. X    PutLine0(prompt_line,0,"Copies to: ");
  1406. X
  1407. X    fflush(stdout);
  1408. X
  1409. X    if (optionally_enter(cc_field, prompt_line, 11, FALSE, FALSE) == -1) {
  1410. X      ClearLine(prompt_line-1);
  1411. X      ClearLine(prompt_line);
  1412. X      
  1413. X      error("Mail not sent.");
  1414. X      return(0);
  1415. X    }
  1416. X    
  1417. X    /** The following test is that if the build_address routine had
  1418. X        reason to rewrite the entry given, then, if we're mailing only
  1419. X        print the new Cc line below the old one.  If we're not, then
  1420. X        assume we're in screen mode and replace the incorrect entry on
  1421. X        the line above where we are (e.g. where we originally prompted
  1422. X        for the Cc: field).
  1423. X    **/
  1424. X
  1425. X    if (build_address(strip_commas(cc_field), addressII)) {
  1426. X      PutLine1(prompt_line, 11, "%s", addressII);
  1427. X      if ((strcmp(editor, "builtin") != 0 && strcmp(editor, "none") != 0)
  1428. X          || copy_message)
  1429. X        sleep(2);
  1430. X    }
  1431. X
  1432. X    if (strlen(address) + strlen(addressII) > VERY_LONG_STRING) {
  1433. X      dprint(2, (debugfile, 
  1434. X        "String length of \"To:\" + \"Cc\" too long! (get_copies)\n"));
  1435. X      error("Too many people. Copies ignored.");
  1436. X      sleep(2);
  1437. X      cc_field[0] = '\0';
  1438. X    }
  1439. X
  1440. X    return(1);        /* everything looks okay! */
  1441. X}
  1442. X    
  1443. Xint
  1444. Xcopy_the_msg(is_a_response)
  1445. Xint *is_a_response;
  1446. X{
  1447. X    /** Returns True iff the user wants to copy the message being
  1448. X        replied to into the edit buffer before invoking the editor! 
  1449. X        Sets "is_a_response" to true if message is a response...
  1450. X    **/
  1451. X
  1452. X    int answer = FALSE;
  1453. X
  1454. X    if (forwarding)
  1455. X      answer = TRUE;
  1456. X    else if (strlen(to) > 0 && !mail_only) {  /* predefined 'to' line! */
  1457. X      if (auto_copy) 
  1458. X        answer = TRUE;
  1459. X      else 
  1460. X        answer = (want_to("Copy message? (y/n) ", 'n') == 'y');
  1461. X      *is_a_response = TRUE;
  1462. X    }
  1463. X
  1464. X    return(answer);
  1465. X}
  1466. X
  1467. Xstatic int to_line, to_col;
  1468. X
  1469. Xdisplay_to(address)
  1470. Xchar *address;
  1471. X{
  1472. X    /** Simple routine to display the "To:" line according to the
  1473. X        current configuration (etc)                   
  1474. X     **/
  1475. X    register int open_paren;
  1476. X
  1477. X    to_line = mail_only ? 3 : LINES - 3;
  1478. X    to_col = mail_only ? 0 : COLUMNS - 50;
  1479. X    if (names_only)
  1480. X      if ((open_paren = chloc(address, '(')) > 0) {
  1481. X        if (open_paren < chloc(address, ')')) {
  1482. X          output_abbreviated_to(address);
  1483. X          return;
  1484. X        } 
  1485. X      }
  1486. X    if(mail_only)
  1487. X      if(strlen(address) > 80)
  1488. X        PutLine1(to_line, to_col, "To: (%s)", 
  1489. X            tail_of_string(address, 75));
  1490. X      else
  1491. X        PutLine1(to_line, to_col, "To: %s", address);
  1492. X    else if (strlen(address) > 45) 
  1493. X      PutLine1(to_line, to_col, "To: (%s)", 
  1494. X          tail_of_string(address, 40));
  1495. X    else {
  1496. X      if (strlen(address) > 30) 
  1497. X        PutLine1(to_line, to_col, "To: %s", address);
  1498. X      else
  1499. X        PutLine1(to_line, to_col, "          To: %s", address);
  1500. X      CleartoEOLN();
  1501. X    }
  1502. X}
  1503. X
  1504. Xoutput_abbreviated_to(address)
  1505. Xchar *address;
  1506. X{
  1507. X    /** Output just the fields in parens, separated by commas if need
  1508. X        be, and up to COLUMNS-50 characters...This is only used if the
  1509. X        user is at level BEGINNER.
  1510. X    **/
  1511. X
  1512. X    char newaddress[LONG_STRING];
  1513. X    register int iindex, newindex = 0, in_paren = 0;
  1514. X
  1515. X    iindex = 0;
  1516. X
  1517. X    while (newindex < 55 && iindex < strlen(address)) {
  1518. X      if (address[iindex] == '(') in_paren++;
  1519. X      else if (address[iindex] == ')') { 
  1520. X        in_paren--;
  1521. X        if (iindex < strlen(address)-4) {
  1522. X          newaddress[newindex++] = ',';
  1523. X          newaddress[newindex++] = ' ';
  1524. X        }
  1525. X      }
  1526. X      
  1527. X      /* copy if in_paren but not at the opening outer parens */
  1528. X      if (in_paren && !(address[iindex] == '(' && in_paren == 1))
  1529. X          newaddress[newindex++] = address[iindex];
  1530. X         
  1531. X      iindex++;
  1532. X    }
  1533. X
  1534. X    newaddress[newindex] = '\0';
  1535. X
  1536. X    if (mail_only)
  1537. X      if (strlen(newaddress) > 80) 
  1538. X        PutLine1(to_line, to_col, "To: (%s)", 
  1539. X           tail_of_string(newaddress, 60));
  1540. X      else
  1541. X        PutLine1(to_line, to_col, "To: %s", newaddress);
  1542. X    else if (strlen(newaddress) > 50) 
  1543. X       PutLine1(to_line, to_col, "To: (%s)", 
  1544. X           tail_of_string(newaddress, 40));
  1545. X     else {
  1546. X       if (strlen(newaddress) > 30)
  1547. X         PutLine1(to_line, to_col, "To: %s", newaddress);
  1548. X       else
  1549. X         PutLine1(to_line, to_col, "          To: %s", newaddress);
  1550. X       CleartoEOLN();
  1551. X     }
  1552. X
  1553. X    return;
  1554. X}
  1555. SHAR_EOF
  1556. chmod 0444 src/mailmsg1.c || echo "restore of src/mailmsg1.c fails"
  1557. echo "x - extracting src/mailmsg2.c (Text)"
  1558. sed 's/^X//' << 'SHAR_EOF' > src/mailmsg2.c &&
  1559. X
  1560. Xstatic char rcsid[] = "@(#)$Id: mailmsg2.c,v 2.29 89/03/25 21:46:43 syd Exp $";
  1561. X
  1562. X/*******************************************************************************
  1563. X *  The Elm Mail System  -  $Revision: 2.29 $   $State: Exp $
  1564. X *
  1565. X *             Copyright (c) 1986, 1987 Dave Taylor
  1566. X *             Copyright (c) 1988, 1989 USENET Community Trust
  1567. X *******************************************************************************
  1568. X * Bug reports, patches, comments, suggestions should be sent to:
  1569. X *
  1570. X *    Syd Weinstein, Elm Coordinator
  1571. X *    elm@dsinc.UUCP            dsinc!elm
  1572. X *
  1573. X *******************************************************************************
  1574. X * $Log:    mailmsg2.c,v $
  1575. X * Revision 2.29  89/03/25  21:46:43  syd
  1576. X * Initial 2.2 Release checkin
  1577. X * 
  1578. X *
  1579. X ******************************************************************************/
  1580. X
  1581. X/** Interface to allow mail to be sent to users.  Part of ELM  **/
  1582. X
  1583. X
  1584. X#include "headers.h"
  1585. X#include <errno.h>
  1586. X
  1587. Xextern int errno;
  1588. Xextern char version_buff[];
  1589. X
  1590. Xchar *error_name(), *error_description(), *strip_parens();
  1591. Xchar *strcat(), *strcpy();
  1592. Xchar *format_long(), *strip_commas(), *tail_of_string(); 
  1593. X
  1594. Xunsigned long sleep();
  1595. X
  1596. X#ifdef SITE_HIDING 
  1597. X char *get_ctime_date();
  1598. X#endif
  1599. XFILE *write_header_info();
  1600. X
  1601. X/* these are all defined in the mailmsg1.c file! */
  1602. X
  1603. Xextern char subject[SLEN], in_reply_to[SLEN], expires[SLEN], 
  1604. X            action[SLEN], priority[SLEN], reply_to[SLEN], to[VERY_LONG_STRING], 
  1605. X        cc[VERY_LONG_STRING], expanded_to[VERY_LONG_STRING], 
  1606. X        expanded_cc[VERY_LONG_STRING], user_defined_header[SLEN],
  1607. X        bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING];
  1608. X
  1609. X
  1610. Xint  gotten_key;
  1611. Xchar *bounce_off_remote();
  1612. X
  1613. Xmail(copy_msg, edit_message, form)
  1614. Xint  copy_msg, edit_message, form;
  1615. X{
  1616. X    /** Given the addresses and various other miscellany (specifically, 
  1617. X        'copy-msg' indicates whether a copy of the current message should 
  1618. X        be included, 'edit_message' indicates whether the message should 
  1619. X        be edited) this routine will invoke an editor for the user and 
  1620. X        then actually mail off the message. 'form' can be YES, NO, or
  1621. X        MAYBE.  YES=add "Content-Type: mailform" header, MAYBE=add the
  1622. X        M)ake form option to last question, and NO=don't worry about it!
  1623. X        Also, if 'copy_msg' = FORM, then grab the form temp file and use
  1624. SHAR_EOF
  1625. echo "End of part 16"
  1626. echo "File src/mailmsg2.c is continued in part 17"
  1627. echo "17" > s2_seq_.tmp
  1628. exit 0
  1629.  
  1630.